/*************************************************************************
 *                    free                     *
 *************************************************************************
 *************************************************************************
 * Filename	   : table_sdt.c
 * Description   : This file contains the methods in processing sdt
 * Version	   : 1.0
 * History       :
 free 2013-4-15  Create
 *************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include "section.h"
#include "table_pat.h"
#include "table_sdt.h"

#define SERVICE_DESCRIPTOR_TAG 		0x48
#define SDT_PACKAGE_ID				0x11
#define SDT_TABLE_ID				0x42

/*************************************************************************
 * Function Name : FreeServiceDescriptor
 * Description   : free one service descriptor and its pucServiceProviderName,pucServiceName if necessary
 * Parameters    :
 * Returns       :
 **************************************************************************/
static void FreeServiceDescriptor(SERVICE_DESCRIPTOR **ppstDescriptor)
{
	if ((*ppstDescriptor)->pucServiceProviderName != NULL )
	{
		free((*ppstDescriptor)->pucServiceProviderName);
	}
	if ((*ppstDescriptor)->pucServiceName != NULL )
	{
		free((*ppstDescriptor)->pucServiceName);
	}
	free(*ppstDescriptor);
	(*ppstDescriptor) = NULL;
}

/*************************************************************************
 * Function Name : GetServiceDescriptor
 * Description   : get service descriptor from a byte array
 * Parameters    :
 * Returns       :
 **************************************************************************/
static SERVICE_DESCRIPTOR *GetServiceDescriptor(unsigned char *pucBuffer, int iDescriptorSize)
{
	int iReadCounter = 0;
	int iReadIndex = 0;
	int iArraySize = 0;
	SERVICE_DESCRIPTOR *pstDescriptor = NULL;

	pstDescriptor = (SERVICE_DESCRIPTOR *)malloc(sizeof(SERVICE_DESCRIPTOR));
	pstDescriptor->pucServiceProviderName = NULL;
	pstDescriptor->pucServiceName = NULL;
	while (iReadCounter < iDescriptorSize)
	{
		pstDescriptor->descriptor_tag = pucBuffer[0];
		pstDescriptor->descriptor_length = pucBuffer[1];
		if (pstDescriptor->descriptor_tag == SERVICE_DESCRIPTOR_TAG)
		{
			pstDescriptor->service_type = pucBuffer[2];
			pstDescriptor->service_provider_name_length = pucBuffer[3];
			iArraySize = pstDescriptor->service_provider_name_length + 1;
			pstDescriptor->pucServiceProviderName = (unsigned char *)malloc(iArraySize * sizeof(unsigned char));
			memset(pstDescriptor->pucServiceProviderName, 0, iArraySize);
			iReadIndex = 4;
			memcpy(pstDescriptor->pucServiceProviderName, pucBuffer + iReadIndex, iArraySize - 1);
			iReadIndex += (iArraySize - 1);
			pstDescriptor->service_name_length = pucBuffer[iReadIndex];
			iArraySize = pstDescriptor->service_name_length + 1;
			pstDescriptor->pucServiceName = (unsigned char *)malloc(iArraySize * sizeof(unsigned char));
			memset(pstDescriptor->pucServiceName, 0, iArraySize);
			iReadIndex++;
			memcpy(pstDescriptor->pucServiceName, pucBuffer + iReadIndex, iArraySize - 1);
			return pstDescriptor;
		}
		pucBuffer += pstDescriptor->descriptor_length + 2;
		iReadCounter += pstDescriptor->descriptor_length + 2;
	}
	FreeServiceDescriptor(&pstDescriptor);
	return NULL ;
}

/*************************************************************************
 * Function Name : ParseService
 * Description   : get one service from a byte array
 * Parameters    :
 * Returns       :
 **************************************************************************/
static void ParseService(SERVICE *pstService, unsigned char *pucBuffer)
{
	pstService->service_id = (pucBuffer[0] << 8) | pucBuffer[1];
	pstService->reserved_future_use = (pucBuffer[2] >> 2) & 0x3f;
	pstService->EIT_schedule_flag = (pucBuffer[2] >> 1) & 0x01;
	pstService->EIT_present_following_flag = pucBuffer[2] & 0x01;
	pstService->running_status = (pucBuffer[3] >> 5) & 0x07;
	pstService->free_CA_mode = (pucBuffer[3] >> 4) & 0x01;
	pstService->descriptors_loop_length = ((pucBuffer[3] & 0x0f) << 8) | pucBuffer[4];
	pstService->pstServiceDescriptors = GetServiceDescriptor(pucBuffer + 5, pstService->descriptors_loop_length);
	pstService->pstNext = NULL;
}

/*************************************************************************
 * Function Name : GetServiceList
 * Description   : get all service contained in one SDT section and link them to a list
 * Parameters    :
 * Returns       :
 **************************************************************************/
static SERVICE *GetServiceList(unsigned char *pucBuffer, int iServiceSize)
{
	int iReadCounter = 0;
	SERVICE *pstServices = NULL;
	SERVICE *pstTemp = NULL;
	SERVICE *pstNew = NULL;

	while (iReadCounter < iServiceSize)
	{
		pstNew = (SERVICE *)malloc(sizeof(SERVICE));
		ParseService(pstNew, pucBuffer);
		if (pstServices == NULL )
		{
			pstServices = pstNew;
		}
		else
		{
			pstTemp->pstNext = pstNew;
		}
		pstTemp = pstNew;
		iReadCounter += (5 + pstNew->descriptors_loop_length);
		pucBuffer += (5 + pstNew->descriptors_loop_length);
	}
	return pstServices;
}

/*************************************************************************
 * Function Name : GetServiceList
 * Description   : free all services and their descriptors in the list
 * Parameters    :
 * Returns       :
 **************************************************************************/
static void FreeServiceList(SERVICE **ppstServices)
{
	SERVICE *pstTemp = NULL;
	while ((*ppstServices) != NULL )
	{
		pstTemp = *ppstServices;
		*ppstServices = (*ppstServices)->pstNext;
		FreeServiceDescriptor(&(pstTemp->pstServiceDescriptors));
		free(pstTemp);
	}
}

/*************************************************************************
 * Function Name : GetSectionSdt
 * Description   : get one SDT section from byte array
 * Parameters    :
 * Returns       :
 **************************************************************************/
static SECTION_SDT *GetSectionSdt(unsigned char *pucBuffer)
{
	SECTION_SDT *pstSectionSdt = (SECTION_SDT *)malloc(sizeof(SECTION_SDT));
	pstSectionSdt->table_id = pucBuffer[0];
	pstSectionSdt->section_syntax_indicator = (pucBuffer[1] >> 7) & 0x01;
	pstSectionSdt->reserved_future_use1 = (pucBuffer[1] >> 6) & 0x1;
	pstSectionSdt->reserved1 = (pucBuffer[1] >> 4) & 0x3;
	pstSectionSdt->section_length = ((pucBuffer[1] & 0x0F) << 8) | pucBuffer[2];
	pstSectionSdt->transport_stream_id = (pucBuffer[3] << 8) | pucBuffer[4];
	pstSectionSdt->reserved2 = (pucBuffer[5] >> 6) & 0x03;
	pstSectionSdt->version_number = (pucBuffer[5] >> 1) & 0x1F;
	pstSectionSdt->current_next_indicator = pucBuffer[5] & 0x01;
	pstSectionSdt->section_number = pucBuffer[6];
	pstSectionSdt->last_section_number = pucBuffer[7];
	pstSectionSdt->original_network_id = (pucBuffer[8] << 8) | pucBuffer[9];
	pstSectionSdt->reserved_future_use2 = pucBuffer[10];
	pstSectionSdt->pstServices = GetServiceList(pucBuffer + 11, pstSectionSdt->section_length - 8 - 4);
	pstSectionSdt->pstNext = NULL;
	return pstSectionSdt;
}

/*************************************************************************
 * Function Name : GetTableSdt
 * Description   : get all SDT section from byte array and link them to a list
 * Parameters    :
 * Returns       :
 **************************************************************************/
SECTION_SDT *GetTableSdt(FILE *pInputFile, long int liFirstPackageOffset, int iPackageLength)
{
	SECTION_SDT *pstTableSdt = NULL;
	SECTION_SDT *pstTemp = NULL;
	SECTION_SDT *pstNew = NULL;
	SECTION *pstSection = NULL;
	SECTION *pstSectionTemp = NULL;

	pstSection = GetSectionList(pInputFile, liFirstPackageOffset, iPackageLength, SDT_PACKAGE_ID, SDT_TABLE_ID);
	pstSectionTemp = pstSection;
	while (pstSectionTemp != NULL )
	{
		pstNew = GetSectionSdt(pstSectionTemp->pucData);
		if (pstTableSdt == NULL )
		{
			pstTableSdt = pstNew;
		}
		else
		{
			pstTemp->pstNext = pstNew;
		}
		pstTemp = pstNew;
		pstSectionTemp = pstSectionTemp->pstNext;
	}
	FreeSectionList(&pstSection);
	return pstTableSdt;
}

/*************************************************************************
 * Function Name : FreeTableSdt
 * Description   : free all the sdt sections and their services and descriptors in a SDT table
 * Parameters    :
 * Returns       :
 **************************************************************************/
void FreeTableSdt(SECTION_SDT **ppstSectionSdt)
{
	SECTION_SDT *pstTemp = NULL;
	while ((*ppstSectionSdt) != NULL )
	{
		pstTemp = *ppstSectionSdt;
		*ppstSectionSdt = (*ppstSectionSdt)->pstNext;
		FreeServiceList(&(pstTemp->pstServices));
		free(pstTemp);
	}
}

